<template>
  <div class="lexicon-tab">
    <!-- 工具栏 + 表格滚动 + 底部分页/全选 -->
    <TabScaffold :current="pagination.current" :pageSize="pagination.pageSize" :total="total"
      :pageCount="pageData.length" :checkAll="checkAll" @update:current="(v) => pagination.current = v"
      @update:pageSize="(v) => pagination.pageSize = v" @check-all="onCheckAll">
      <!-- 工具栏（左） -->
      <template #toolbar-left>
        <a-space>
          <a-button @click="onAdd">新增</a-button>
          <a-button :disabled="!selectedRowKeys.length" @click="onBatchDelete">批量删除</a-button>
          <a-button @click="onImport">导入</a-button>
          <a-button :loading="exporting" @click="onExport">导出</a-button>
        </a-space>
      </template>

      <!-- 工具栏（右） -->
      <template #toolbar-right>
        <a-input-search v-model:value="keyword" placeholder="输入搜索" enter-button="搜索" allow-clear @search="onSearch"
          style="width: 260px" />
      </template>

      <!-- 表格区域 -->
      <a-table :columns="columns" :data-source="pageData" row-key="id" :pagination="false" :row-selection="rowSelection"
        bordered class="table" @change="onTableChange" :customRow="onCustomRow">
        <template #bodyCell="{ column, record }">
          <template v-if="column.key === 'name'">
            <!-- 改为纯文本，颜色黑色 -->
            <span class="name-text">{{ record.name }}</span>
          </template>

          <template v-else-if="column.key === 'status'">
            <span :class="['status', record.status === 'enabled' ? 'on' : 'off']">
              {{ record.status === 'enabled' ? '启用' : '停用' }}
            </span>
          </template>

          <template v-else-if="column.key === 'actions'">
            <span class="ops">
              <a @click="onEdit(record)">编辑</a>
              <span class="sep">|</span>
              <a @click="onDelete(record)" style="color: #ff4d4f;">删除</a>
              <span class="sep">|</span>

              <!-- 停用 -->
              <a v-if="record.status === 'enabled'" :class="{ disabled: isRowLoading(record.id) }"
                @click="!isRowLoading(record.id) && onToggleStatus(record, 'disable')">停用</a>
              <span v-else class="disabled">停用</span>

              <span class="sep">|</span>

              <!-- 启用 -->
              <a v-if="record.status === 'disabled'" :class="{ disabled: isRowLoading(record.id) }"
                @click="!isRowLoading(record.id) && onToggleStatus(record, 'enable')">启用</a>
              <span v-else class="disabled">启用</span>
            </span>
          </template>
        </template>
      </a-table>
    </TabScaffold>

    <!-- 导入弹窗 -->
    <ImportModal v-model:modelValue="importVisible" title="敏感词导入" accept=".csv,.json" tip="说明：支持 CSV 或 JSON（UTF-8）。"
      @import="doImport" />

    <!-- 导出弹窗 -->
    <ExportModal v-model:modelValue="exportVisible" v-model:format="exportFormat" :loading="exporting" title="敏感词导出"
      @confirm="doExport" />
  </div>
</template>

<script lang="ts" setup>
import { computed, reactive, ref, onMounted, watch, createVNode } from "vue";
import { useRoute, useRouter } from "vue-router";
import { Modal, message } from "ant-design-vue";
import type { Key } from "ant-design-vue/es/table/interface";
import { ExclamationCircleOutlined } from "@ant-design/icons-vue";

/* === 封装组件 === */
import TabScaffold from "@/components/TabScaffold.vue";
import ImportModal from "@/components/ImportModal.vue";
import ExportModal from "@/components/ExportModal.vue";

import {
  listSensativeWords,
  importSensativeWords,
  removeSensativeWord,
  removeSensativeWords,
  updateSensativeWord,
  getFirstNodeNameIdMap,
  listSecondNodesByFirstCodeStrict,
} from "@/api/modules/sensativeWords.api";

/** 时间串转为 YYYY-MM-DD HH:mm:ss */
function formatDateTime(input?: string): string {
  if (!input) return "";
  const s = String(input);
  if (/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(\.\d+)?Z?$/.test(s)) {
    return s.replace("T", " ").replace(/(\.\d+)?Z?$/, "");
  }
  try {
    const d = new Date(s);
    if (!isNaN(d.getTime())) {
      const pad = (n: number) => String(n).padStart(2, "0");
      return `${d.getFullYear()}-${pad(d.getMonth() + 1)}-${pad(d.getDate())} ${pad(d.getHours())}:${pad(d.getMinutes())}:${pad(d.getSeconds())}`;
    }
  } catch { }
  return s;
}

/* 路由上下文 */
const router = useRouter();
const route = useRoute();
const TAB_KEY = "sensitive";
const withTab = (extra: Record<string, any> = {}) => ({ tab: (route.query.tab as string) || TAB_KEY, ...extra });

/* 当前用户中文名 */
const currentUserCname: string = (window as any).__MicroAPP__?._user?.userInfo?.cname ?? "";

/* 行类型 */
type Status = "enabled" | "disabled";
type Row = {
  id: number | string;
  name: string;
  category1: string;
  category2: string;
  matchMode: string;
  handleMode: string;
  owner: string;
  addTime: string;
  status: Status;
};

/* 查询状态 */
const keyword = ref("");
const pagination = reactive({ current: 1, pageSize: 10 });
const total = ref(0);
const sortState = reactive<{ field: string; order: "ascend" | "descend" | null }>({ field: "", order: null });

/* 排序映射 */
const SORT_FIELD_MAP: Record<string, string> = {
  id: "id",
  name: "sensative_word",
  category1: "firstcode",
  category2: "secondnode",
  matchMode: "",
  handleMode: "sensative_type",
  owner: "user_created.last_name",
  addTime: "date_created",
  status: "status",
};

/* 数据 */
const pageData = ref<Row[]>([]);

/* 行内 loading（防重复点击） */
const loadingMap = reactive<Record<string | number, boolean>>({});
const isRowLoading = (id: string | number) => !!loadingMap[id];
const setRowLoading = (id: string | number, v: boolean) => { if (v) loadingMap[id] = true; else delete loadingMap[id]; };

/** 列表 */
async function fetchData() {
  try {
    const k = keyword.value.trim();
    const filter: Record<string, any> = { status: { _neq: "archived" } };
    if (k) {
      filter._or = [
        { sensative_word: { _contains: k } },
        { sensative_type: { _contains: k } },
        { user_created: { email: { _contains: k } } },
      ];
    }

    let sort = "id";
    if (sortState.field && sortState.order) {
      const backend = SORT_FIELD_MAP[sortState.field];
      if (backend) sort = (sortState.order === "descend" ? "-" : "") + backend;
    }

    const { data, meta } = await listSensativeWords({
      page: pagination.current,
      limit: pagination.pageSize,
      sort,
      filter,
      meta: ["filter_count", "total_count"],
    });
    if ((!data || data.length === 0) && pagination.current > 1) {
      pagination.current -= 1;
      await fetchData();
      return;
    }

    pageData.value = (data || []).map((it: any) => {
      const statusNorm: Status = ["published", "active", "enabled"].includes(String(it.status || "").toLowerCase())
        ? "enabled"
        : "disabled";
      return {
        id: it.id,
        name: it.sensative_word ?? "",
        category1: it.firstcode?.first_query ?? "",
        category2: it.secondnode?.second_query ?? "",
        matchMode: "包含匹配",
        handleMode: it.sensative_type ?? "",
        owner: currentUserCname,
        addTime: formatDateTime(it.date_created),
        status: statusNorm,
      } as Row;
    });

    total.value = (meta?.filter_count as number | undefined) ?? (meta?.total_count as number | undefined) ?? 0;
  } catch (e: any) {
    const msg = e?.response?.data?.errors?.[0]?.message || e?.message || JSON.stringify(e?.response?.data || e);
    console.error("[Directus] 获取失败：", msg, e);
    message.error(msg || "数据加载失败");
  }
}
onMounted(fetchData);

/* 搜索/排序/分页 */
function onSearch() { pagination.current = 1; fetchData(); }
watch(keyword, (v) => { if (v.trim() === "") { pagination.current = 1; fetchData(); } });
function onTableChange(_: any, __: any, sorter: any) {
  const s = Array.isArray(sorter) ? sorter[0] : sorter;
  const field = (s?.columnKey ?? s?.field ?? "") as string;
  const order = (s?.order ?? null) as "ascend" | "descend" | null;
  sortState.field = field; sortState.order = order;
  pagination.current = 1; fetchData();
}
watch(() => [pagination.current, pagination.pageSize], () => fetchData());

/* 选择 & 全选 */
const selectedRowKeys = ref<Key[]>([]);
const rowSelection = computed(() => ({ selectedRowKeys: selectedRowKeys.value, onChange: (keys: Key[]) => (selectedRowKeys.value = keys) }));
const currentPageKeys = computed<Key[]>(() => pageData.value.map((r) => r.id));
const checkAll = computed(() => currentPageKeys.value.length > 0 && currentPageKeys.value.every((k) => selectedRowKeys.value.includes(k)));
function onCheckAll(e: any) {
  const checked = e?.target?.checked;
  if (checked) selectedRowKeys.value = Array.from(new Set([...selectedRowKeys.value, ...currentPageKeys.value]));
  else selectedRowKeys.value = selectedRowKeys.value.filter((k) => !currentPageKeys.value.includes(k));
}

/* 列定义 */
const columns = [
  { title: "名称", dataIndex: "name", key: "name", sorter: false, columnKey: "name", width: 160 },
  { title: "分类1", dataIndex: "category1", key: "category1", width: 120, sorter: false, columnKey: "category1" },
  { title: "分类2", dataIndex: "category2", key: "category2", width: 120, sorter: false, columnKey: "category2" },
  { title: "匹配方式", dataIndex: "matchMode", key: "matchMode", width: 140, sorter: false, columnKey: "matchMode" },
  { title: "处理方式", dataIndex: "handleMode", key: "handleMode", width: 140, sorter: false, columnKey: "handleMode" },
  { title: "添加人", dataIndex: "owner", key: "owner", width: 120, sorter: false, columnKey: "owner" },
  { title: "添加时间", dataIndex: "addTime", key: "addTime", width: 200, sorter: false, columnKey: "addTime" },
  { title: "状态", dataIndex: "status", key: "status", width: 120, sorter: false, columnKey: "status" },
  { title: "操作", key: "actions", width: 240 },
];

/* 跳转 */
function onAdd() {
  router.push({ path: "/sensitiveLexiconPage", query: withTab({ mode: "add", backTo: "tab" }) });
}
function onNameClick(row: Row) {
  router.push({ path: "/sensitiveLexiconInfo", query: withTab({ id: String(row.id) }) });
}
function onEdit(row: Row) {
  router.push({
    path: "/sensitiveLexiconPage",
    query: withTab({ mode: "edit", backTo: "tab", row: encodeURIComponent(JSON.stringify(row)) }),
  });
}

/* 整行点击跳转（操作列内点击除外） */
function onCustomRow(record: Row) {
  return {
    onClick: (e: MouseEvent) => {
      const target = e.target as HTMLElement;
      if (target && target.closest('.ops')) return; // 操作列内点击不跳详情
      onNameClick(record); // 其余区域点击整行跳详情
    },
    style: { cursor: "pointer" },
  } as any;
}

/* 删除（单个） */
async function onDelete(row: Row) {
  Modal.confirm({
    title: "确认删除该敏感词？",
    content: `将删除：${row.name || row.id}`,
    okText: "删除",
    okType: "danger",
    cancelText: "取消",
    icon: createVNode(ExclamationCircleOutlined),
    async onOk() {
      try {
        await removeSensativeWord(row.id);
        selectedRowKeys.value = selectedRowKeys.value.filter((k) => k !== row.id);
        await fetchData();
        message.success("删除成功");
      } catch (e: any) {
        const msg = e?.response?.data?.errors?.[0]?.message || e?.message || JSON.stringify(e?.response?.data || e);
        message.error(msg || "删除失败");
      }
    },
    onCancel() { message.info("已取消删除"); },
  });
}

/* 批量删除 */
async function onBatchDelete() {
  if (!selectedRowKeys.value.length) { message.warning("请先选择要删除的记录"); return; }
  Modal.confirm({
    title: "确认批量删除？",
    content: `将删除选中的 ${selectedRowKeys.value.length} 条记录，此操作不可撤销。`,
    okText: "删除",
    okType: "danger",
    cancelText: "取消",
    icon: createVNode(ExclamationCircleOutlined),
    async onOk() {
      try {
        await removeSensativeWords(selectedRowKeys.value as (string | number)[]);
        selectedRowKeys.value = [];
        await fetchData();
        message.success("删除成功");
      } catch (e: any) {
        const msg = e?.response?.data?.errors?.[0]?.message || e?.message || JSON.stringify(e?.response?.data || e);
        message.error(msg || "批量删除失败");
      }
    },
    onCancel() { message.info("已取消删除"); },
  });
}

/* 启用/停用 */
async function onToggleStatus(row: Row, action: "enable" | "disable") {
  if (isRowLoading(row.id)) return;
  try {
    setRowLoading(row.id, true);
    await updateSensativeWord(row.id, { status: action === "enable" ? "published" : "draft" });
    row.status = action === "enable" ? "enabled" : "disabled";
    message.success(action === "enable" ? "已启用" : "已停用");
  } catch (e: any) {
    const msg = e?.response?.data?.errors?.[0]?.message || e?.message || JSON.stringify(e?.response?.data || e);
    message.error(msg || "操作失败");
  } finally { setRowLoading(row.id, false); }
}

/* ====================== 导出 ====================== */
const exportVisible = ref(false);
const exportFormat = ref<"csv" | "json">("csv");
const exporting = ref(false);

function onExport() { exportVisible.value = true; }
function closeExport() { exportVisible.value = false; }

/** 拉取“全部符合当前筛选/排序”的数据 */
async function fetchAllForExport() {
  const k = keyword.value.trim();
  const filter: Record<string, any> = { status: { _neq: "archived" } };
  if (k) {
    filter._or = [
      { sensative_word: { _contains: k } },
      { sensative_type: { _contains: k } },
      { user_created: { email: { _contains: k } } },
    ];
  }
  let sort = "id";
  if (sortState.field && sortState.order) {
    const backend = SORT_FIELD_MAP[sortState.field];
    if (backend) sort = (sortState.order === "descend" ? "-" : "") + backend;
  }

  const pageSize = 500;
  let page = 1;
  const all: any[] = [];
  while (true) {
    const { data } = await listSensativeWords({ page, limit: pageSize, sort, filter, meta: [] });
    const chunk = data || [];
    all.push(...chunk);
    if (chunk.length < pageSize) break;
    page += 1;
  }
  return all;
}

/** 拉取“仅选中 id 列表”的数据 */
async function fetchSelected(ids: (string | number)[]) {
  if (!ids.length) return [];
  const all: any[] = [];
  const pageSize = 200;
  for (let i = 0; i < ids.length; i += pageSize) {
    const batch = ids.slice(i, i + pageSize);
    const { data } = await listSensativeWords({
      page: 1, limit: batch.length, sort: "id",
      filter: { id: { _in: batch }, status: { _neq: "archived" } }, meta: [],
    });
    all.push(...(data || []));
  }
  return all;
}
function toCnStatus(s?: string) {
  return ["published", "active", "enabled"].includes(String(s || "").toLowerCase()) ? "启用" : "停用";
}
function rowsToCsv(rows: Array<Record<string, any>>) {
  const header = ["名称", "分类1", "分类2", "匹配方式", "处理方式", "状态"];
  const csvRows = rows.map(r => [
    r["名称"] ?? "", r["分类1"] ?? "", r["分类2"] ?? "",
    r["匹配方式"] ?? "", r["处理方式"] ?? "", r["状态"] ?? "",
  ]);
  const escape = (s: any) => {
    const str = String(s ?? "");
    return /[",\n]/.test(str) ? `"${str.replace(/"/g, '""')}"` : str;
  };
  return header.join(",") + "\n" + csvRows.map(r => r.map(escape).join(",")).join("\n");
}
function downloadBlob(text: string, name: string, mime = "text/plain;charset=utf-8") {
  const blob = new Blob([text], { type: mime });
  const url = URL.createObjectURL(blob);
  const a = document.createElement("a");
  a.href = url; a.download = name; a.click();
  URL.revokeObjectURL(url);
}
async function doExport() {
  if (exporting.value) return;
  exporting.value = true;
  const key = "sensative-export";
  message.loading({ content: "正在导出...", key, duration: 0 });
  try {
    const ids = selectedRowKeys.value as (string | number)[];
    const hasSelection = ids && ids.length > 0;
    const raw = hasSelection ? await fetchSelected(ids) : await fetchAllForExport();
    const rows = (raw || []).map((it: any) => ({
      名称: it.sensative_word ?? "",
      分类1: it.firstcode?.first_query ?? "",
      分类2: it.secondnode?.second_query ?? "",
      匹配方式: "包含匹配",
      处理方式: it.sensative_type ?? "",
      状态: toCnStatus(it.status),
    }));
    if (exportFormat.value === "json") {
      downloadBlob(JSON.stringify(rows, null, 2), "敏感词导出.json", "application/json;charset=utf-8");
    } else {
      downloadBlob(rowsToCsv(rows), "敏感词导出.csv", "text/csv;charset=utf-8");
    }
    message.success({ content: `导出成功`, key });
    closeExport();
  } catch (e: any) {
    message.error({ content: e?.message || "导出失败", key });
  } finally {
    exporting.value = false;
  }
}

/* ====================== 导入 ====================== */
const importVisible = ref(false);
function onImport() { importVisible.value = true; }

/** 名称ID 缓存 */
let firstName2Id: Record<string, string | number> | null = null;
const secondMapCache: Record<string | number, Record<string, string | number>> = {};
async function ensureFirstMap() { if (!firstName2Id) firstName2Id = await getFirstNodeNameIdMap(); }
async function getSecondMap(firstId: string | number) {
  if (secondMapCache[firstId]) return secondMapCache[firstId];
  const { data = [] } = await listSecondNodesByFirstCodeStrict({
    firstCodeId: firstId, limit: 1000, fields: ["id", "second_query"], sort: ["id"]
  });
  const map: Record<string, string | number> = {};
  (data || []).forEach((it: any) => { const name = String(it?.second_query ?? "").trim(); if (name) map[name] = it.id; });
  secondMapCache[firstId] = map; return map;
}
async function rowToPayloadCn(row: { 名称?: string; 分类1?: string; 分类2?: string; 匹配方式?: string; 处理方式?: string; 状态?: string; }) {
  await ensureFirstMap();
  const name = String(row.名称 ?? "").trim(); if (!name) return null;
  const sensative_type = String(row.处理方式 ?? "").trim(); if (!sensative_type) return null;
  const st = String(row.状态 ?? "").trim().toLowerCase();
  let status: "published" | "draft" = "draft";
  if (["启用", "enabled", "published", "true"].includes(st)) status = "published";
  if (["停用", "disabled", "draft", "false"].includes(st)) status = "draft";

  let firstcode: string | number | null = null;
  let secondnode: string | number | null = null;
  const cat1Name = String(row.分类1 ?? "").trim();
  const cat2Name = String(row.分类2 ?? "").trim();

  if (cat1Name && firstName2Id && firstName2Id[cat1Name] != null) {
    firstcode = firstName2Id[cat1Name];
    if (cat2Name) {
      const secondMap = await getSecondMap(firstcode);
      if (secondMap[cat2Name] != null) secondnode = secondMap[cat2Name];
    }
  }
  return { sensative_word: name, sensative_type, status, firstcode: firstcode ?? null, secondnode: secondnode ?? null };
}

/* 解析 CSV/JSON */
function parseCsvToRows(csv: string) {
  const lines = csv.split(/\r?\n/).filter((l) => l.trim().length);
  if (!lines.length) throw new Error("空文件");
  const header = lines[0].split(",").map((s) => s.trim());
  const idx = {
    名称: header.indexOf("名称") >= 0 ? header.indexOf("名称") : header.indexOf("name"),
    分类1: header.indexOf("分类1") >= 0 ? header.indexOf("分类1") : header.indexOf("category1"),
    分类2: header.indexOf("分类2") >= 0 ? header.indexOf("分类2") : header.indexOf("category2"),
    匹配方式: header.indexOf("匹配方式") >= 0 ? header.indexOf("匹配方式") : header.indexOf("matchMode"),
    处理方式: header.indexOf("处理方式") >= 0 ? header.indexOf("处理方式") : header.indexOf("handleMode"),
    状态: header.indexOf("状态") >= 0 ? header.indexOf("状态") : header.indexOf("status"),
  };
  if (Object.values(idx).some((i) => i < 0)) throw new Error("模板不匹配：需包含 名称,分类1,分类2,匹配方式,处理方式,状态");

  const rows = lines.slice(1).map((l) => {
    const arr = l.split(",");
    return {
      名称: (arr[idx.名称] || "").trim(),
      分类1: (arr[idx.分类1] || "").trim(),
      分类2: (arr[idx.分类2] || "").trim(),
      匹配方式: (arr[idx.匹配方式] || "").trim(),
      处理方式: (arr[idx.处理方式] || "").trim(),
      状态: (arr[idx.状态] || "").trim(),
    };
  });
  return rows.filter((r) => r.名称);
}
function parseJsonToRows(jsonText: string) {
  const arr = JSON.parse(jsonText);
  if (!Array.isArray(arr)) throw new Error("JSON 顶层必须是数组");
  return arr.map((it: any, i: number) => {
    const r = {
      名称: it.名称 ?? it.name ?? "",
      分类1: it.分类1 ?? it.category1 ?? "",
      分类2: it.分类2 ?? it.category2 ?? "",
      匹配方式: it.匹配方式 ?? it.matchMode ?? "包含匹配",
      处理方式: it.处理方式 ?? it.sensative_type ?? it.handleMode ?? "",
      状态: it.状态 ?? it.status ?? "",
    };
    if (!String(r.名称).trim()) throw new Error(`第 ${i + 1} 行缺少“名称”`);
    if (!String(r.处理方式).trim()) throw new Error(`第 ${i + 1} 行缺少“处理方式”`);
    return r;
  });
}

async function doImport(file: File) {
  try {
    const nameLower = file.name.toLowerCase();
    const text = await readFileText(file);
    const rows = nameLower.endsWith(".csv")
      ? parseCsvToRows(text)
      : nameLower.endsWith(".json")
        ? parseJsonToRows(text)
        : (() => { throw new Error("不支持的文件类型：仅支持 CSV 或 JSON"); })();

    const payloads = [];
    for (const row of rows) {
      const p = await rowToPayloadCn(row as any);
      if (p) payloads.push(p);
    }
    if (!payloads.length) throw new Error("没有可导入的数据");

    await importSensativeWords(payloads as any);
    message.success(`导入成功`);
    importVisible.value = false;
    await fetchData();
  } catch (e: any) {
    const code =
      e?.response?.data?.errors?.[0]?.extensions?.code ||
      e?.data?.errors?.[0]?.extensions?.code ||
      e?.code;
    if (code === "RECORD_NOT_UNIQUE") {
      message.error("检测到重复的值");
    } else {
      message.error(e?.message || "导入失败");
    }
  }
}

/* 读取文件文本 */
function readFileText(f: File): Promise<string> {
  return new Promise((resolve, reject) => {
    const reader = new FileReader();
    reader.onerror = () => reject(new Error("文件读取失败"));
    reader.onabort = () => reject(new Error("文件读取被中断"));
    reader.onload = () => resolve(String(reader.result || ""));
    reader.readAsText(f, "utf-8");
  });
}
</script>

<style lang="less" scoped>
.lexicon-tab {
  min-width: 0;
}

.table :deep(.name-link) {
  color: #1677ff;
  cursor: pointer;
  text-decoration: none;
}

/* 名称：改为黑色文本 */
.table :deep(.name-text) {
  color: #000;
}

.table :deep(.status.on) {
  color: #1f7a1f;
}

.table :deep(.status.off) {
  color: #999;
}

.table :deep(.ops .sep) {
  margin: 0 8px;
  color: #999;
}

.table :deep(.ops .disabled) {
  color: #c5c5c5 !important;
  cursor: not-allowed;
  pointer-events: none;
}
</style>
